package com.bingxue.edu.management.education.service;

import com.bingxue.edu.common.enums.ClassStatusEnum;
import com.bingxue.edu.common.enums.EnrollmentStatusEnum;
import com.bingxue.edu.common.enums.StudentClassEnum;
import com.bingxue.edu.framework.mybatis.base.BaseService;
import com.bingxue.edu.management.education.mapper.ClassStatMapper;
import com.bingxue.edu.management.education.model.entity.ClassInfo;
import com.bingxue.edu.management.education.model.entity.ClassStat;
import com.bingxue.edu.management.education.model.query.ClassQuery;
import com.bingxue.edu.management.report.model.resp.ClassStatResp;
import com.bingxue.edu.management.system.model.entity.table.EmployeeTableDef;
import com.mybatisflex.core.query.QueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;

import static com.bingxue.edu.management.education.model.entity.table.ClassInfoTableDef.CLASS_INFO;
import static com.bingxue.edu.management.education.model.entity.table.ClassStatTableDef.CLASS_STAT;
import static com.bingxue.edu.management.education.model.entity.table.EnrollmentTableDef.ENROLLMENT;
import static com.bingxue.edu.management.education.model.entity.table.LessonStudentTableDef.LESSON_STUDENT;
import static com.bingxue.edu.management.system.model.entity.table.EmployeeTableDef.EMPLOYEE;
import static com.bingxue.edu.management.system.model.entity.table.OrgTableDef.ORG;

@Slf4j
@Service
@RequiredArgsConstructor
public class ClassStatService extends BaseService<ClassStatMapper, ClassStat> {
    private final ClassInfoService classInfoService;
    private final EnrollmentService enrollmentService;
    private final LessonStudentService lessonStudentService;

    public List<ClassStatResp> getClassStatList(ClassQuery query) {
        EmployeeTableDef leadTeacher = EMPLOYEE.as("lead");
        EmployeeTableDef assistTeacher = EMPLOYEE.as("assist");
        QueryWrapper queryWrapper = QueryWrapper.create()
                .select(
                        CLASS_STAT.ALL_COLUMNS,
                        CLASS_INFO.GRADE,
                        CLASS_INFO.LEAD_TEACHER_ID,
                        CLASS_INFO.ASSIST_TEACHER_ID,
                        CLASS_INFO.NAME.as(ClassStatResp::getClassName),
                        leadTeacher.NAME.as(ClassStatResp::getLeadTeacherName),
                        assistTeacher.NAME.as(ClassStatResp::getAssistTeacherName),
                        ORG.ID.as(ClassStatResp::getCampusId),
                        ORG.NAME.as(ClassStatResp::getCampusName)
                )
                .from(CLASS_STAT)
                .leftJoin(CLASS_INFO).on(CLASS_STAT.CLASS_ID.eq(CLASS_INFO.ID))
                .leftJoin(leadTeacher).on(CLASS_INFO.LEAD_TEACHER_ID.eq(leadTeacher.ID))
                .leftJoin(assistTeacher).on(CLASS_INFO.ASSIST_TEACHER_ID.eq(assistTeacher.ID))
                .leftJoin(ORG).on(CLASS_INFO.CAMPUS_ID.eq(ORG.ID))
                .where(CLASS_INFO.NAME.like(query.getKeyword()))
                .and(CLASS_INFO.SUBJECT.eq(query.getSubject()))
                .and(CLASS_INFO.CLASS_YEAR.eq(query.getClassYear()))
                .and(CLASS_INFO.CLASS_CATEGORY.eq(query.getClassCategory()))
                .and(CLASS_INFO.CLASS_STATUS.ne(ClassStatusEnum.CLOSED))
                .and(CLASS_INFO.CAMPUS_ID.eq(query.getCampusId()))
                .and(CLASS_INFO.CLASSROOM_ID.eq(query.getClassroomId()))
                .and(CLASS_INFO.TERM.eq(query.getTerm()))
                .and(CLASS_INFO.GRADE.in(query.getGrade()))
                .and(CLASS_INFO.LEAD_TEACHER_ID.eq(query.getLeadTeacherId()))
                .and(CLASS_INFO.ASSIST_TEACHER_ID.eq(query.getAssistTeacherId()))
                .and(CLASS_INFO.SALE_STATUS.eq(query.getSaleStatus()));
        return this.listAs(queryWrapper, ClassStatResp.class);
    }

    public ClassStat getClassStatById(String classId) {
        ClassStat classStat = this.getOne(CLASS_STAT.CLASS_ID.eq(classId));
        if (classStat == null) {
            classStat = newClassStat(classId);
        }
        return classStat;
    }

    /**
     * 计算各个班级的三率
     * 续报率只计算长期班级
     */
    public void computePerformanceRate() {
        // 选取未开课、上课中的班级进行计算
        List<ClassInfo> classInfoList = classInfoService.queryChain()
//                .where(CLASS_INFO.CLASS_STATUS.in(ClassStatusEnum.WAITING, ClassStatusEnum.TEACHING))
                .list();
        for (ClassInfo classInfo: classInfoList) {
            try {
                computePerformanceRate(classInfo);
            } catch (Exception e) {
                log.warn("班级数据计算失败: {}", e.getMessage());
            }
        }
    }

    /**
     * 计算单个班级三率
     */
    public void computePerformanceRate(ClassInfo classInfo) {
        ClassStat classStat = newClassStat(classInfo.getId());
        classStat.setPlannedStudentCount(classInfo.getPlanStudentCount());
        classStat.setPassedStudentCount(classInfo.getPassedStudentCount());
        classStat.setRenewalStudentCount(getRenewalStudentCount(classInfo.getId()));
        classStat.setCancelStudentCount(getCancelStudentCount(classInfo.getId()));
        classStat.setRetainedStudentCount(getRetainedStudentCount(classInfo.getId()));

        classStat.setTotalLessonCount(getTotalLessonCount(classInfo.getId()));
        classStat.setCancelLessonCount(getCancelLessonCount(classInfo.getId()));
        classStat.setComputeDate(LocalDateTime.now());

        computeRate(classStat);

        saveOrUpdateStat(classStat);
    }

    /**
     * 插入或更新
     */
    private void saveOrUpdateStat(ClassStat classStat) {
        ClassStat excitedRecord = this.getOne(CLASS_STAT.CLASS_ID.eq(classStat.getClassId()));
        if (excitedRecord == null) {
            this.save(classStat);
        } else {
            classStat.setId(excitedRecord.getId());
            this.updateById(classStat);
        }
    }

    /**
     * 查询班级续报人数
     */
    private Integer getRenewalStudentCount(String classId) {
        QueryWrapper queryWrapper = QueryWrapper.create()
                .from(ENROLLMENT)
                .where(ENROLLMENT.CLASS_ID.eq(classId))
                .and(ENROLLMENT.RENEWAL_CLASS_IDS.isNotNull())
                .and(ENROLLMENT.STATUS.eq(EnrollmentStatusEnum.NORMAL));
        return (int)enrollmentService.count(queryWrapper);
    }

    /**
     * 查询班级留存人数
     * 留存人数=续报人数+当期退费学员中在下期报名的人数
     */
    private Integer getRetainedStudentCount(String classId) {
        return (int) enrollmentService.queryChain()
                .where(ENROLLMENT.CLASS_ID.eq(classId))
                .and(ENROLLMENT.RENEWAL_CLASS_IDS.isNotNull())
                .and(ENROLLMENT.CANCEL_LESSON_COUNT.lt(ENROLLMENT.ENROLL_LESSON_COUNT))
                .count();
    }


    /**
     * 查询班级退费人数
     */
    private Integer getCancelStudentCount(String classId) {
        QueryWrapper queryWrapper = QueryWrapper.create()
                .from(ENROLLMENT)
                .where(ENROLLMENT.CLASS_ID.eq(classId))
                .and(ENROLLMENT.STATUS.eq(EnrollmentStatusEnum.CANCELED))
                .and(ENROLLMENT.CANCEL_LESSON_COUNT.lt(ENROLLMENT.ENROLL_LESSON_COUNT));
        return (int)enrollmentService.count(queryWrapper);

//        QueryWrapper queryWrapper = QueryWrapper.create()
//                .from(ENROLL_CHANGE_RECORD)
////                .where(ENROLL_CHANGE_RECORD.ORIGINAL_CLASS_ID.eq(classId))
//                .where(ENROLL_CHANGE_RECORD.CHANGE_TYPE.eq(ChangeTypeEnum.CANCEL_CLASS))
//                .where(ENROLL_CHANGE_RECORD.LAST_LESSON_CLASS_ID.eq(classId))
//                .and(ENROLL_CHANGE_RECORD.OPERATE_STATUS.eq(OperateStatusEnum.SUCCESS));
//        return (int)enrollChangeRecordService.count(queryWrapper);
    }

    /**
     * 计算班级总课次数
     */
    public Integer getTotalLessonCount(String classId){
        QueryWrapper queryWrapper = QueryWrapper.create()
                .select()
                .where(LESSON_STUDENT.LESSON_CLASS_ID.eq(classId))
//                .and(LESSON_STUDENT.SIGN_IN_STATUS.eq(true))
                ;
        return (int) lessonStudentService.count(queryWrapper);
    }

    /**
     * 计算班级退课课次数
     */
    public Integer getCancelLessonCount(String classId){
        QueryWrapper queryWrapper  = QueryWrapper.create()
                .select()
                .where(LESSON_STUDENT.LESSON_CLASS_ID.eq(classId))
                .and(LESSON_STUDENT.STUDENT_CLASS_STATUS.in(StudentClassEnum.CANCEL_CLASS, StudentClassEnum.CANCEL_LESSON));
        return (int)lessonStudentService.count(queryWrapper);

    }

    /**
     * 计算四率
     */
    private void computeRate(ClassStat classStat) {
        Integer plannedStudentCount = classStat.getPlannedStudentCount();
        Integer renewalStudentCount = classStat.getRenewalStudentCount();
        Integer passedStudentCount = classStat.getPassedStudentCount();
        Integer cancelStudentCount = classStat.getCancelStudentCount();
        Integer retainedStudentCount = classStat.getRetainedStudentCount();
        int allStudentCount = passedStudentCount + cancelStudentCount;

        double fullRate = 0.0;
        double renewalRate = 0.0;
        double cancelRate = 0.0;
        double retainedRate = 0.0;

        if (plannedStudentCount != 0) {
            fullRate = passedStudentCount * 100.0/plannedStudentCount;
        }

        if (passedStudentCount != 0) {
            renewalRate = renewalStudentCount * 100.0/passedStudentCount;
        }

        if (allStudentCount != 0) {
            cancelRate = cancelStudentCount * 100.0/allStudentCount;
        }

        if (allStudentCount != 0) {
            // 9 / 7
            // 9： retainedStudentCount 把这学期退费的人算进去了
            retainedRate = retainedStudentCount * 100.0/allStudentCount;
        }

        classStat.setFullRate(fullRate);
        classStat.setRenewalRate(renewalRate);
        classStat.setCancelRate(cancelRate);
        classStat.setRetainedRate(retainedRate);
    }


    private ClassStat newClassStat(String classId) {
        ClassStat classStat = new ClassStat();
        classStat.setClassId(classId);
        classStat.setPlannedStudentCount(0);
        classStat.setPassedStudentCount(0);
        classStat.setRenewalStudentCount(0);
        classStat.setCancelStudentCount(0);
        classStat.setRetainedStudentCount(0);
        classStat.setTotalLessonCount(0);
        classStat.setCancelLessonCount(0);
        classStat.setFullRate(0.0);
        classStat.setCancelRate(0.0);
        classStat.setRenewalRate(0.0);
        classStat.setRetainedRate(0.0);
        classStat.setComputeDate(LocalDateTime.now());
        return classStat;
    }

}
